package com.google.gautoard;

import com.google.apphosting.api.ApiProxy;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 *
 * @author dras
 */
public class ListenServlet extends HttpServlet {

  private static final long serialVersionUID = 1L;
  private static final Logger log = Logger.getLogger(ServiceServlet.class.getName());
  long timeout = 5000L; // stop looping when this many ms are left in the request timer

  /**
   * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
   *
   * @param request servlet request
   * @param response servlet response
   * @throws ServletException if a servlet-specific error occurs
   * @throws IOException if an I/O error occurs
   */
  protected void processRequest(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    try (PrintWriter out = response.getWriter()) {
      // this connection stays open until timeout or a value is sent back
      // (as the result of a change)
      response.setContentType("text/plain");

      ArrayList<EventBean> ebinitial = this.arrangeRequest(request);
      log.log(Level.INFO, "got {0} keys to listen for", ebinitial.size());
      while (ApiProxy.getCurrentEnvironment().getRemainingMillis() > timeout) {
        // do we have new info to hand back?
        // walk the ArrayList, load each EventBean, compare values against original
        // ArrayList<EventBean>ebcurrent = dc.getValues(ebinitial);
        for (EventBean ebcompinitial : ebinitial) {
          // for now, just check to see if the value changed
          // TODO(dras): add ability to check if timestamp changed
          EventBean eb = new EventBean(ebcompinitial.getUniqueKey() + ".get");
          String curval = eb.getValue();

          log.log(Level.INFO, "init={0} current={1}", new Object[] {ebcompinitial.getValue(),
              curval});
          if (!ebcompinitial.getValue().equals(curval)) {
            // send the new value back & close
            response.setStatus(HttpServletResponse.SC_OK);
            response.getWriter().write(eb.toString());
            response.getWriter().flush();
            out.close();
            return;
          }
        }
        // TODO(dras): how to detect if client disconnected?
        try {
          log.log(Level.INFO, "zzz...");
          Thread.sleep(2000);
        } catch (InterruptedException e) {
        }
      }
      // if you get to this point (timeout), the value didn't change
      response.setStatus(HttpServletResponse.SC_NO_CONTENT);
      // returns 204, no content, which tells the client to
      // immediately reconnect
    }
  }

  /**
   * Transforms the HTTP Request into a set of EventBeans
   * 
   * @param req The servlet request
   * @return An ArrayList of EventBeans parsed from the HTTP Request
   * @throws IOException
   */
  public ArrayList<EventBean> arrangeRequest(HttpServletRequest req) throws IOException {
    log.log(Level.INFO, "Starting arrangeRequest");
    ArrayList<EventBean> ebs = new ArrayList<>();
    String keyprefix = "";
    for (Enumeration<String> paramNames = req.getParameterNames(); paramNames.hasMoreElements();) {
      String name = paramNames.nextElement();
      log.log(Level.INFO, "got a name:{0}", name);
      if ("auth".equals(name)) {
        continue; // the auth isn't a bean
      }
      if ("keyprefix".equals(name)) {
        log.log(Level.INFO, "set prefix:{0}", name);
        keyprefix = req.getParameter("keyprefix");
        continue;
      }
      EventBean eb = new EventBean(keyprefix + name);
      eb.load();
      ebs.add(eb);
      log.log(Level.INFO, "arrange listener:{0}", eb);
    }
    return ebs;
  }

  /**
   * Handles the HTTP <code>GET</code> method.
   *
   * @param request servlet request
   * @param response servlet response
   * @throws ServletException if a servlet-specific error occurs
   * @throws IOException if an I/O error occurs
   */
  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    processRequest(request, response);
  }

  /**
   * Handles the HTTP <code>POST</code> method.
   *
   * @param request servlet request
   * @param response servlet response
   * @throws ServletException if a servlet-specific error occurs
   * @throws IOException if an I/O error occurs
   */
  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    processRequest(request, response);
  }
}
